<template>
    <div>
        <div id="container">
         <div style="height: 100%;width: 180px;">
            <div id="stencil"></div>
         </div>
         <div style="max-width: calc(100% - 180px);max-height: calc(100vh - 130px);overflow: scroll;">
           <div id="graph-container"></div>
         </div>
        </div>
    </div>
</template>
<script>
// import '@/components/graph-antv6/insertCss.css'
import { Graph, Shape } from '@antv/x6'
import { Stencil } from '@antv/x6-plugin-stencil'
import { Transform } from '@antv/x6-plugin-transform'
import { Selection } from '@antv/x6-plugin-selection'
import { Snapline } from '@antv/x6-plugin-snapline'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { Clipboard } from '@antv/x6-plugin-clipboard'
import { History } from '@antv/x6-plugin-history'


// 初始化连接桩信息
const ports = {
  groups: {
    top: {
      position: 'top',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    right: {
      position: 'right',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    bottom: {
      position: 'bottom',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
    left: {
      position: 'left',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: '#5F95FF',
          strokeWidth: 1,
          fill: '#fff',
          style: {
            visibility: 'hidden',
          },
        },
      },
    },
  },
  items: [
    {
      group: 'top',
    },
    {
      group: 'right',
    },
    {
      group: 'bottom',
    },
    {
      group: 'left',
    },
  ],
}
// 为节点(矩形节点)设置连接桩(上下左右)
Graph.registerNode(
  'custom-rect',
  {
    inherit: 'rect',
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    tools: ['node-editor'],
    ports: { ...ports },
  },
  true,
)
// 为节点(多边形节点)设置连接桩(上下左右)
Graph.registerNode(
  'custom-polygon',
  {
    inherit: 'polygon',
    width: 66,
    height: 36,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    tools: ['node-editor'],
    ports: {
      ...ports
    },
  },
  true,
)
// 为节点(圆形节点)设置连接桩(上下左右)
Graph.registerNode(
  'custom-circle',
  {
    inherit: 'circle',
    width: 45,
    height: 45,
    attrs: {
      body: {
        strokeWidth: 1,
        stroke: '#5F95FF',
        fill: '#EFF4FF',
      },
      text: {
        fontSize: 12,
        fill: '#262626',
      },
    },
    tools: ['node-editor'],
    ports: { ...ports },
  },
  true,
)
// 为节点(图片节点)设置连接桩(上下左右)
Graph.registerNode(
  'custom-image',
  {
    inherit: 'rect',
    width: 52,
    height: 52,
    markup: [
      {
        tagName: 'rect',
        selector: 'body',
      },
      {
        tagName: 'image',
      },
      {
        tagName: 'text',
        selector: 'label',
      },
    ],
    attrs: {
      body: {
        stroke: '#5F95FF',
        fill: '#5F95FF',
      },
      image: {
        width: 26,
        height: 26,
        refX: 13,
        refY: 16,
      },
      label: {
        refX: 3,
        refY: 2,
        textAnchor: 'left',
        textVerticalAnchor: 'top',
        fontSize: 12,
        fill: '#fff',
      },
    },
    tools: ['node-editor'],
    ports: { ...ports },
  },
  true,
)

export default {
    components: {
    },
    data() {
        return {
            // 图实例
            graph:undefined,
        }
    },
    mounted() {
    },
    methods: {
      // 获取节点数据
      getGraphData(){
        console.log('获取画布中的数据',this.graph.toJSON());
        return this.graph.toJSON().cells
      },
      // 创建相关容器(左侧拖拽工具,右侧画布容器)
      preWork() {
        let stencilDom = document.getElementById('stencil');
        // 不存在才创还能相关容器
        if (stencilDom == null) {
          const container = document.getElementById('container')
          const stencilContainer = document.createElement('div')
          stencilContainer.id = 'stencil'
          const graphContainer = document.createElement('div')
          graphContainer.id = 'graph-container'
          container.appendChild(stencilContainer)
          container.appendChild(graphContainer)
        }
      
      },
      // 流程图初始化事件
      async initGraph (data) {
        // 画布实例未初始化时才需要初始化(不可多次初始化,功能会错乱)
        if (this.graph == undefined) {
          //await this.preWork()
          this.graph = await new Graph({
              container: document.getElementById('graph-container'),
              grid: true,
              mousewheel: {
                  enabled: true,
                  zoomAtMousePosition: true,
                  modifiers: 'ctrl',
                  minScale: 0.5,
                  maxScale: 3,
              },
              connecting: {
                  router: 'manhattan',
                  connector: {
                  name: 'rounded',
                  args: {
                      radius: 8,
                  },
                  },
                  anchor: 'center',
                  connectionPoint: 'anchor',
                  allowBlank: false,
                  snap: {
                      radius: 20,
                  },
                  createEdge() {
                      return new Shape.Edge({
                          label: '',
                          tools: ['edge-editor'],
                          attrs: {
                              line: {
                                  stroke: '#A2B1C3',
                                  strokeWidth: 2,
                                  targetMarker: {
                                      name: 'block',
                                      width: 12,
                                      height: 8,
                                  },
                              },
                          },
                          zIndex: 0,
                      })
                  },
                  // 验证连接是否可用
                  validateConnection({ targetMagnet }) {
                      console.log('validateConnection',targetMagnet);
                      return !!targetMagnet
                  },
              },
              highlighting: {
                  magnetAdsorbed: {
                  name: 'stroke',
                  args: {
                      attrs: {
                      fill: '#5F95FF',
                      stroke: '#5F95FF',
                      },
                  },
                  },
              },
          })
          // 使用插件扩展功能
          await this.graph
              .use(
                  new Transform({
                      resizing: true,
                      rotating: true,
                  }),
              )
              .use(
                  new Selection({
                      rubberband: true,
                      showNodeSelectionBox: true,
                  }),
              )
              .use(new Snapline())
              .use(new Keyboard())
              .use(new Clipboard())
              .use(new History())
          await this.initStencil()
          await this.bindKeys()
          
          // node的鼠标进入事件(显示连线桩)
          await this.graph.on('node:mouseenter', () => {
              const container = document.getElementById('graph-container')
              const ports = container.querySelectorAll(
                  '.x6-port-body',
              ) 
              this.showPorts(ports, true)
          })
          // node的鼠标离开事件(不显示连线桩)
          await this.graph.on('node:mouseleave', () => {
              const container = document.getElementById('graph-container')
              const ports = container.querySelectorAll(
                  '.x6-port-body',
              ) 
              this.showPorts(ports, false)
          })
          // 绑定节点的点击事件(用来同步选中节点信息)
          await this.graph.on('node:click', ({ e, x, y, node, view }) => {
            console.log('节点选中事件',node);
            this.$emit('syncNodeInfoMethod',node)
          })
          // 绑定画布空白区域点击事件(用来清空选中节点信息)
          await this.graph.on('blank:click', ({ e}) => {
            console.log('画布空白区域点击事件');
            this.$emit('clearNodeInfo')
          })
        } 
        // 先清空数据, 再渲染
        await this.graph.fromJSON([])
        await this.graph.fromJSON(data)
      },
      // 初始化模版
      async initStencil(){
        const stencil = await new Stencil({
            title: '流程图',
            target: this.graph,
            stencilGraphWidth: 200,
            stencilGraphHeight: 180,
            collapsable: false,
            groups: [
                {
                title: '基础流程图',
                name: 'group1',
                },
            ],
            layoutOptions: {
                columns: 2,
                columnWidth: 80,
                rowHeight: 55,
            },
        })
        document.getElementById('stencil').appendChild(stencil.container)
        // 创建节点(便于拖拽的快捷节点)-矩形-带圆角边框
        const r1 = this.graph.createNode({
            shape: 'custom-rect',
            label: '开始 / 结束',
            attrs: {
                body: {
                    rx: 14,
                    ry: 18,
                    fill:'#d0e0f9', // 背景色
                    stroke:'#6098ed', // 边框色
                    strokeWidth:1.5, // 边框宽度
                },
            },
        })
        // 创建节点-矩形
        const r2 = this.graph.createNode({
            shape: 'custom-rect',
            label: '过程',
            attrs:{
              body:{
                fill:'#e7dfff', // 背景色
                stroke:'#7458dc', // 边框色
                strokeWidth:1.5, // 边框宽度
              }
            }
        })
        // 创建节点-矩形-带圆角边框
        const r3 = this.graph.createNode({
            shape: 'custom-rect',
            attrs: {
                body: {
                    rx: 6,
                    ry: 6,
                    fill:'#bee4be', // 背景色
                    stroke:'#90c790', // 边框色
                    strokeWidth:1.5, // 边框宽度
                },
            },
            label: '可选过程',
        })
        // 创建节点-多边形
        const r4 = this.graph.createNode({
            shape: 'custom-polygon',
            attrs: {
                body: {
                    refPoints: '0,10 10,0 20,10 10,20',
                    fill:'#ffefae', // 背景色
                    stroke:'#facd39', // 边框色
                    strokeWidth:1.5, // 边框宽度
                },
            },
            label: '判断',
        })
        // 创建节点-多边形
        const r5 = this.graph.createNode({
            shape: 'custom-polygon',
            attrs: {
                body: {
                  refPoints: '10,0 40,0 30,20 0,20',
                  fill:'#97ecf363', // 背景色
                  stroke:'#b6e3f3', // 边框色
                  strokeWidth:1.5, // 边框宽度
                },
            },
            label: '数据',
        })
        // 创建节点-圆形
        const r6 = this.graph.createNode({
            shape: 'custom-circle',
            label: '连接',
        })
        stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
      },
      // 绑定快捷键与事件
      bindKeys(){
        // #region 快捷键与事件
        // 复制
        this.graph.bindKey(['meta+c', 'ctrl+c'], () => {
            console.log('meta+c');
            const cells = this.graph.getSelectedCells()
            if (cells.length) {
                this.graph.copy(cells)
            }
            return false
        })
        // 剪切
        this.graph.bindKey(['meta+x', 'ctrl+x'], () => {
          console.log('meta+x');
            const cells = this.graph.getSelectedCells()
            if (cells.length) {
                this.graph.cut(cells)
            }
            return false
        })
        // 粘贴
        this.graph.bindKey(['meta+v', 'ctrl+v'], () => {
          console.log('meta+v');
            if (!this.graph.isClipboardEmpty()) {
                const cells = this.graph.paste({ offset: 32 })
                this.graph.cleanSelection()
                this.graph.select(cells)
            }
            return false
        })

        // 撤回
        this.graph.bindKey(['ctrl+z'], () => {
          console.log('meta+z');
            if (this.graph.canUndo()) {
                this.graph.undo()
            }
            return false
        })
        this.graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
          console.log('meta+shift+z');
            if (this.graph.canRedo()) {
                this.graph.redo()
            }
            return false
        })

        // select all
        this.graph.bindKey([ 'ctrl+alt+a'], () => {
          console.log('meta+a');
            const nodes = this.graph.getNodes()
            if (nodes) {
                this.graph.select(nodes)
            }
        })

        // 删除
        this.graph.bindKey('backspace', () => {
            const cells = this.graph.getSelectedCells()
            if (cells.length) {
                this.graph.removeCells(cells)
            }
        })
        // 删除
        this.graph.bindKey('del', () => {
            const cells = this.graph.getSelectedCells()
            if (cells.length) {
                this.graph.removeCells(cells)
            }
        })

        // 放大画布
        this.graph.bindKey(['ctrl+up', 'meta+1'], () => {
            const zoom = this.graph.zoom()
            if (zoom < 1.5) {
                this.graph.zoom(0.1)
            }
        })
        // 缩小画布
        this.graph.bindKey(['ctrl+down', 'meta+2'], () => {
            const zoom = this.graph.zoom()
            if (zoom > 0.5) {
                this.graph.zoom(-0.1)
            }
        })
      },
      // 控制连接桩显示/隐藏
      showPorts(ports,show){
        for (let i = 0, len = ports.length; i < len; i += 1) {
            ports[i].style.visibility = show ? 'visible' : 'hidden'
        }
      },
    },
    
}
</script>
<style scoped>
#container {
    display: flex;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
>>> #stencil {
    width: 180px;
    min-width: 180px !important;
    height: calc(100vh - 115px);
    position: fixed !important;
    border-right: 1px solid #dfe3e8;
}
>>> #graph-container {
    width: 1400px !important;
    height: 1400px !important;
    /* width: calc(100% - 180px) !important;
    height: 100%;
    min-width: 1400px !important;
    min-height: 1400px; */
}
.x6-widget-stencil  {
    background-color: #fff;
    width: 180px;
}
.x6-widget-stencil-title {
    background-color: #fff;
}
>>> .x6-widget-stencil-content {
  height: max-content !important;
}
.x6-widget-stencil-group-title {
    background-color: #fff !important;
}
.x6-widget-transform {
    margin: -1px 0 0 -1px;
    padding: 0px;
    border: 1px solid #239edd;
}
.x6-widget-transform > div {
    border: 1px solid #239edd;
}
.x6-widget-transform > div:hover {
    background-color: #3dafe4;
}
.x6-widget-transform-active-handle {
    background-color: #3dafe4;
}
.x6-widget-transform-resize {
    border-radius: 0;
}
.x6-widget-selection-inner {
    border: 1px solid #239edd;
}
.x6-widget-selection-box {
    opacity: 0;
}
>>> .x6-node-tool, >>> .x6-edge-tool {
  text-align: left;
}
</style>